#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LEN_PATTERN     80
#define LEN_NUM         4
#define NUM_PATTERN     60
struct pattern_item
{
    char str[LEN_PATTERN];

    char pattern_using; /* it is a string pattern or a item pattern. */
    char pattern;
    char padding;
    size_t resize;
};


struct ulog_patterns {
    struct pattern_item items[NUM_PATTERN];
    int num_items;
};


void ulog_pattern_add_string(struct pattern_item* patterns, 
        int nmax, int* pnum, 
        /*const */char* s, size_t size)
{
    int idx;
    if(*pnum >= nmax){
        /* pattern item full. do not need to store. */
    }
    else {
        /* If two consecutive are string, then concatenation. */
        if(*pnum > 0 && !(patterns[(*pnum)-1].pattern_using)) {
            idx = (*pnum)-1;
            _ustrncat(patterns[idx].str, LEN_PATTERN, s, size);
        }
        else {
            idx = *pnum;
            (*pnum) ++ ;
            patterns[idx].pattern_using = 0;
            _ustrncpy(patterns[idx].str, LEN_PATTERN, s, size);
        }
    }
}


void ulog_pattern_add(struct pattern_item* patterns, 
        int nmax, int* pnum, 
        char p, char padding, size_t width)
{
    int idx;
    if(*pnum >= nmax){
        /* pattern item full. do not need to store. */
    }
    else {
        idx = *pnum;
        (*pnum) ++ ;
        patterns[idx].pattern_using = 1;
        patterns[idx].pattern = p;
        patterns[idx].padding = padding;
        patterns[idx].resize = width;
    }
}


int ulog_pattern_parse(const char* src, struct pattern_item patterns[], int nmax)
{
    int retn = 0;

    char str[LEN_PATTERN];
    size_t size_str;
    const char* tmp = src;

    while(*tmp != '\0') {
        /* read string. */
        size_str = 0;
        while(*tmp != '%' && *tmp != '\0') {
            /* if tmp store string length not enough, it would be cut. */
            if(size_str < LEN_PATTERN) {
                str[size_str] = *tmp;
                size_str ++;
            }
            tmp ++ ;
        }

        if(size_str > 0) {
            ulog_pattern_add_string(patterns, nmax, &retn, str, size_str);
            size_str = 0;
        }

        if(*tmp == '\0') {
            break;
        }

        /* *tmp == '%'. */
        size_str = 0;
        str[size_str] = *tmp;
        size_str ++;

        /* read padding and length. */
        tmp ++ ;
        char padding = 0;
        size_t width = 0;
        if(*tmp == '0') {
            padding = 1;
            str[size_str++] = *tmp;
            tmp ++ ;
        }

        int num_digit = 0;
        while(*tmp >= '0' && *tmp <= '9' && num_digit <= (LEN_NUM-1))
        {
            width = width*10 + (*tmp - '0');
            str[size_str++] = *tmp;
            tmp ++ ;
            num_digit ++;
        }

        /* read pattern. */
        switch(*tmp) {
            /* %% parse to a %. */
            case '%':
                if(size_str == 1) {
                    str[0] = '%';
                }
                else {
                    str[size_str++] = *tmp;
                }
                tmp ++;
            break;

            case 'N':
            case 'L':
            case 'F':
            case 'S':
            case 'U':
            case 'D':
            case 'T':
            case 'M':
            case 'P':
                ulog_pattern_add(patterns, nmax, &retn, *tmp, padding, width);
                size_str = 0;
                tmp ++;
            break;

            case '\0':
                ulog_pattern_add_string(patterns, nmax, &retn, str, size_str);
                size_str = 0;
            break;

            default:
                str[size_str++] = *tmp;
                ulog_pattern_add_string(patterns, nmax, &retn, str, size_str);
                size_str = 0;
                tmp ++;
        }
    }

    return retn;
}


int _ulog_generate(char* dest, size_t size, 
        const char* file, const char* function, long line, const char* content, 
        struct ulog_patterns* patterns)
{
    int retn = 0;
    size_t size_used = 0;
    int n = 0;

    #define LEN_TMP 100
    char str_tmp[LEN_TMP];
    struct timeval tv_cur;
    struct timeval tv_count;
    int finished = 0; 
    int i;
    for(i=0; i<patterns->num_items&&!finished; i++) {
        struct pattern_item* item = &patterns->items[i];
        if(!(item->pattern_using)) {
            n = snprintf(dest+size_used, size-size_used, "%s", item->str);
            size_used += n;
        }
        else {
            switch(item->pattern) {
                case 'N':
                if(0 != item->resize) {
                    if(size-size_used > item->resize) {
                        um_strcpy_prefix(dest+size_used, item->resize, file);
                        size_used += item->resize;
                    }
                    else {
                        finished = 1;
                    }
                }
                else {
                    n = snprintf(dest+size_used, size-size_used, "%s", file);
                    size_used += n;
                    if(n >= (size-size_used-1)) {
                        finished = 1;
                    }
                }
                break;

                case 'F':
                if(0 != item->resize) {
                    if(size-size_used > item->resize) {
                        um_strcpy_prefix(dest+size_used, item->resize, function);
                        size_used += item->resize;
                    }
                    else {
                        finished = 1;
                    }
                }
                else {
                    n = snprintf(dest+size_used, size-size_used, "%s", function);
                    size_used += n;
                    if(n >= (size-size_used-1)) {
                        finished = 1;
                    }
                }
                break;

                case 'L':
                snprintf(str_tmp, LEN_TMP, 
                        "%%%s%uld", item->padding?"0":"", item->resize);
                n = snprintf(dest+size_used, size-size_used, str_tmp, line);
                size_used += n;
                break;

                case 'P':
                n = snprintf(dest+size_used, size-size_used, "%s", content);
                size_used += n;
                break;

                case 'D':
                _udatetime_to_string(str_tmp, LEN_TMP, NULL);
                n = snprintf(dest+size_used, size-size_used, "%s", str_tmp);
                size_used += n;
                break;

                case 'M':
                _utv_get_monotonic(&tv_cur);
                tv_count = _utv_count_tv(&klogpro_tv_start, &tv_cur);
                n = snprintf(dest+size_used, size-size_used, 
                        "%04ld:%06ld", tv_count.tv_sec, tv_count.tv_usec);
                size_used += n;
                break;

                case 'T':
                snprintf(str_tmp, LEN_TMP, 
                        "%%%s%ud", item->padding?"0":"", item->resize);
                n = snprintf(dest+size_used, size-size_used, str_tmp, 
                        _ugetidx_easy(klogpro_ptr_getthreadidx, pthread_self()));
                size_used += n;
                break;

                default:
                break;
            }
        }


        if(size_used >= size-1) {
            finished = 1;
        }
    }
    
    return retn;
}


#if JUST_DEFINE
N   FILE_NAME
F   FUNCTION
L   LINE
P   PRINT_CONTENT
D   DATETIME
M   RUNNING_TIMEVAL
T   PTHREAD
#endif


int _ulog_parse_patterns(struct ulog_patterns* patterns, const char* str)
{
    int ret = 0;
    patterns->num_items = 
            ulog_pattern_parse(str, patterns->items, NUM_PATTERN);

    return ret;
}














#if 0
int main()
{
    #define LEN_LINE    1024
    char str[LEN_LINE] = "[%10P%0123N%12s456]%%debug%P%";
    
    #define NUM_PATTERNS    100
    struct pattern_item patterns[NUM_PATTERNS];


    int n = ulog_pattern_parse(str, patterns, NUM_PATTERNS);
    printf("num of patterns : %d.\n", n);
    int i;
    for(i=0; i<n; i++) {
        if(patterns[i].pattern_using) {
            printf("%02d : %c %d %u\n", i, 
                    patterns[i].pattern, patterns[i].padding, patterns[i].resize);
        }
        else {
            printf("%02d : [%s]\n", i, patterns[i].str);
        }
    }
    
    return 0;
}
#endif
